/*
 * Connector.cpp
 *
 *  Created on: 2014. 6. 25.
 *      Author: jjaehuny
 */

#include "Connector.h"

#include <mutex>

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

#include "../Util/PhoenixSingleton.h"
#include "../Util/TaskProcessing.h"

#include "../DataStructure/AutoDeleteTree.h"

#include "Command.h"

namespace Framework { namespace Database {

::Framework::DataStructure::AutoDeleteTree<int, Connector> s_clsConnectorTree;

Connector *
get_connector (int type)
{
	return s_clsConnectorTree.Get(type);
}

struct session;
::Framework::Memory::Lock_Function_new_Pool<session> driver_pool_;

struct session {
	sql::Driver *driver = NULL;
	sql::Connection *con = NULL;
	bool _isConnect = false;

	void Initialize() {}
	void Finalize() {}

	bool Connect(Connector::Param & param) {
		try {
			driver = get_driver_instance();
			con = driver->connect(param.host_.c_str(), param.user_.c_str(), param.pass_.c_str());
			con->setSchema(param.name_.c_str());
		}
		catch(sql::SQLException & e) {

			LOG(INFO) << __PRETTY_FUNCTION__ << " [error code : " << e.getErrorCode() << "]" << " [sqlState : " << e.getSQLState() << "]";
			return false;
		}

		_isConnect = true;
		return true;
	}
};

} /*Database*/ } /*Framework*/



namespace Framework { namespace Database {

Connector::Connector()
{
	// TODO Auto-generated constructor stub

}

Connector::~Connector() {
	// TODO Auto-generated destructor stub
}

bool
Connector::Connect(Param & param)
{
	param_ = param;
	taskProcessing_ = std::unique_ptr<Util::TaskProcessing>(new Util::TaskProcessing(param.count_));

	for (int i = 0; i < param.count_; i++)
	{
		std::shared_ptr<session> session = driver_pool_.NEW();
		if (false == session->Connect(param)) return false;
	}

	s_clsConnectorTree.Regist(param.type_, this);
	return true;
}

void
Connector::Sync_Execute(std::shared_ptr<Command> command)
{
	try {
		std::shared_ptr<session> session = driver_pool_.NEW();
		if (false == session->_isConnect)
		{
			if (false == session->Connect(param_))
			{
				LOG(INFO) << __PRETTY_FUNCTION__ << "connect fail";

				command->set_isSuccess_(false);
				return;
			}
		}
		else
		{
			LOG(INFO) << __PRETTY_FUNCTION__ << "driver is connected";
		}

		std::unique_ptr<sql::Statement> stmt(session->con->createStatement());
		command->set_resultSet_( stmt->executeQuery( command->get_query_() ) );
		command->set_isSuccess_(true);
	}
	catch (sql::SQLException &e)
	{
		LOG(INFO) << "# ERR: SQLException in ";
		LOG(INFO) << "(" << __PRETTY_FUNCTION__ << ") on line " << __LINE__;
		LOG(INFO) << "# ERR: " << e.what();
		LOG(INFO) << " (MySQL error code: " << e.getErrorCode();
		LOG(INFO) << ", SQLState: " << e.getSQLState() << " )";

		command->set_isSuccess_(false);
	}
}

void
Connector::Async_Execute(std::shared_ptr<Command> command)
{
	{
		std::lock_guard<std::mutex> lock(mutex_);
		command_deque_.push_back(command);
	}

	taskProcessing_->PushTask(std::bind(& Connector::async, this));
}

void
Connector::async()
{
	LOG(INFO) << __PRETTY_FUNCTION__;

	std::shared_ptr<Command> command;
	{
		std::lock_guard<std::mutex> lock(mutex_);
		command = command_deque_.front();
		command_deque_.pop_front();
	}

	if (NULL == command.get())
		return;


	this->Sync_Execute(command->CAPTURE());
	if (NULL != command->get_OnResponse_())
	{
		command->get_OnResponse_()(command->CAPTURE());
	}
}

} /*Database*/ } /*Framework*/
